Docker Compose를 활용한 ERPNext 배포 및 운영 안내서

Docker Compose를 활용한 ERPNext 배포 및 운영 안내서

1. ERPNext와 Docker Compose의 시너지

1.1 ERPNext: 현대 비즈니스를 위한 오픈소스 중추 신경망

ERPNext는 단순한 전사적 자원 관리(ERP) 소프트웨어를 넘어, 기업 운영의 모든 측면을 통합 관리하는 ’중앙 신경 시스템(central nervous system)’으로 기능한다.1 인도 소프트웨어 기업 Frappe Technologies가 개발한 이 시스템은 회계, 재고 관리, 제조, 고객 관계 관리(CRM), 프로젝트 관리, 인사(HR) 등 비즈니스에 필요한 거의 모든 기능을 단일 플랫폼에 통합하여 제공한다.1

ERPNext의 가장 큰 특징은 100% 자유-오픈 소스 소프트웨어(FOSS)라는 점이다.3 GPL-3.0 라이선스 하에 배포되므로, 기업은 고가의 라이선스 비용 부담 없이 강력한 ERP 시스템을 도입할 수 있으며, 데이터에 대한 완전한 소유권을 보장받는다.1 이는 NetSuite, QAD, SAP, Oracle과 같은 독점적 ERP 솔루션과 비교했을 때 비용 투명성과 유연성 측면에서 명확한 차별점을 제공한다.3

기술적으로 ERPNext는 Python과 JavaScript를 주 언어로 사용하며, 견고한 MariaDB 데이터베이스 시스템과 Frappe 프레임워크 위에 구축되어 있다.3 특히 Frappe 프레임워크는 ERPNext의 유연성과 확장성의 핵심이다. 이 프레임워크는 Model-View-Controller(MVC) 아키텍처와 메타데이터 모델링 도구를 기반으로 하여, 개발자가 복잡한 코딩 작업 없이도 비즈니스 요구사항에 맞게 시스템을 신속하게 맞춤 설정할 수 있도록 지원한다.3 이러한 구조 덕분에 ERPNext는 제조업, 유통업, 서비스업은 물론 교육, 의료, 농업, 비영리 단체 등 특정 도메인에 특화된 모듈까지 포괄하는 범용 ERP 솔루션으로 자리매김했다.3

1.2 Docker Compose: 복잡성을 길들이는 오케스트레이션 도구

Docker Compose는 다중 컨테이너 애플리케이션을 정의하고 실행하기 위한 핵심 도구다.5 현대의 복잡한 웹 애플리케이션은 단일 프로세스로 동작하는 경우가 드물다. ERPNext와 같이 웹 서버, 애플리케이션 워커, 데이터베이스, 인메모리 캐시, 메시지 큐 등 여러 서비스가 유기적으로 상호작용해야 하는 시스템의 경우, 각 서비스를 개별 컨테이너로 분리하여 관리하는 것이 효율적이다.7

Docker Compose는 이러한 다중 컨테이너 환경의 복잡성을 docker-compose.yml이라는 단일 YAML 파일을 통해 해결한다.7 이 파일 안에 각 서비스의 구성(사용할 이미지, 포트 매핑, 볼륨, 환경 변수, 서비스 간 의존성 등)을 선언적으로 기술할 수 있다.

docker run 명령어를 서비스마다 개별적으로 실행하고 네트워킹과 볼륨을 수동으로 연결하는 번거로운 과정과 비교할 때, Docker Compose는 다음과 같은 명백한 이점을 제공한다 7:

  • 선언적 구성: 최종적으로 원하는 시스템의 상태를 파일에 명시하면, Compose가 그 상태를 구현하는 방법을 처리한다.

  • 환경의 일관성 및 재현성: 동일한 docker-compose.yml 파일을 사용하면 개발, 스테이징, 프로덕션 등 어떤 환경에서든 동일한 애플리케이션 스택을 정확하게 재현할 수 있다.

  • 자동화된 네트워킹: Compose는 프로젝트 내의 모든 서비스를 위한 격리된 가상 네트워크를 자동으로 생성하여, 서비스들이 서비스 이름을 호스트명으로 사용하여 안전하게 통신할 수 있도록 한다.

  • 간편한 생명주기 관리: 단일 명령어(docker compose up, docker compose down)로 전체 애플리케이션 스택의 생성, 시작, 중지, 제거를 관리할 수 있다.

1.3 왜 ERPNext 배포에 Docker Compose를 사용하는가?

전통적인 ERPNext 설치 방식은 리눅스 서버에 접속하여 여러 의존성 패키지를 설치하고, Frappe의 bench CLI를 사용하기 위해 일련의 스크립트를 순차적으로 실행하는 복잡한 과정을 수반했다.11 이 방식은 운영체제 버전, 설치된 라이브러리 등 호스트 환경에 대한 강한 의존성을 가지며, “내 컴퓨터에서는 되는데, 서버에서는 안 된다(not on my machine)“와 같은 전형적인 배포 문제를 야기했다.11

ERPNext 배포 방식의 패러다임은 이러한 수동적이고 절차적인 스크립트 기반에서, 선언적이고 재현 가능한 컨테이너 기반으로 전환되었다. 이는 단순히 기술 스택의 변화를 넘어, ERP 시스템의 전체 생명주기 관리(Lifecycle Management)에 대한 접근 방식 자체를 혁신했음을 의미한다. 과거의 설치 방식은 호스트 환경에 대한 강한 의존성으로 인해 배포 실패의 주된 원인이 되는 환경 불일치 문제를 야기했다. Docker는 애플리케이션과 그 모든 종속성을 컨테이너라는 격리된 환경에 패키징하여 이 문제를 근본적으로 해결한다.12 ERPNext는 여러 서비스로 구성된 복잡한 시스템이므로 다중 컨테이너 관리가 필수적이며, Docker Compose는 바로 이 다중 컨테이너 환경을 docker-compose.yml이라는 단일 파일로 ’선언’하게 해준다.7 결과적으로 Docker Compose의 도입은 ERPNext 배포를 일회성 ‘설치’ 이벤트에서, 버전 관리가 가능하고(Infrastructure as Code), 언제 어디서든 재현 가능한 ’정의된 상태(defined state)’로 전환시키는 근본적인 변화를 가져왔다.10 이는 DevOps 원칙을 ERP 시스템 관리에 적용한 구체적인 사례라 할 수 있다.

Docker Compose를 활용함으로써 얻는 핵심적인 이점은 다음과 같다:

  • 설치 간소화 및 표준화: docker compose up이라는 단일 명령어로 데이터베이스, Redis 캐시, 웹 서버, 애플리케이션 워커 등 ERPNext 구동에 필요한 모든 구성 요소를 한 번에 실행할 수 있다.10

  • 환경 격리 및 이식성: 모든 종속성이 컨테이너 내에 패키징되므로 호스트 시스템과의 충돌이 없으며, 개발자의 로컬 머신, 테스트 서버, 프로덕션 클라우드 환경 등 Docker가 설치된 곳이라면 어디서든 동일하게 동작한다.12

  • 유지보수 용이성: 백업, 복원, 버전 업데이트와 같은 복잡한 유지보수 작업이 Docker의 표준화된 명령어를 통해 단순하고 예측 가능하게 수행된다.11

  • 유연한 구성 관리: 공식 frappe_docker 리포지토리는 모듈식 오버라이드(override) 파일을 제공하여, 사용자가 필요에 따라 데이터베이스 종류(MariaDB, PostgreSQL), 프록시 사용 여부, HTTPS 활성화 등의 기능을 “플러그인“처럼 손쉽게 조합하여 구성할 수 있게 한다.11

2. 사전 준비: 완벽한 배포를 위한 환경 구축

2.1 시스템 요구사항 분석

ERPNext를 Docker Compose로 원활하게 운영하기 위해서는 적절한 하드웨어 및 소프트웨어 환경을 갖추는 것이 중요하다. 시스템 요구사항은 예상 사용자 수, 데이터의 양, 처리할 트랜잭션의 복잡성 등 워크로드에 따라 달라질 수 있다.15

하드웨어 요구사항은 최소 사양과 안정적인 프로덕션 운영을 위한 권장 사양으로 구분할 수 있다. 소규모 팀이나 개발 환경에서는 최소 사양으로 시작할 수 있지만, 실제 비즈니스 환경에서는 권장 사양 이상을 확보하는 것이 시스템의 응답성과 안정성을 보장하는 데 필수적이다.15

소프트웨어 측면에서는 Linux 운영체제가 가장 안정적이고 권장되는 환경이며, 특히 장기 지원(LTS) 버전의 Ubuntu가 널리 사용된다. 또한, 컨테이너 환경을 구축하고 frappe_docker 리포지토리를 다루기 위한 핵심 도구들을 사전에 설치해야 한다.17

다음 표는 시스템 요구사항을 요약한 것이다. 이를 통해 사용자는 자신의 사용 목적에 맞는 하드웨어 및 소프트웨어 환경을 한눈에 파악하고 준비할 수 있다.

구분최소 사양 (개발/소규모)권장 사양 (프로덕션)근거 자료
CPU2 코어 (1GHz x 2)4+ 코어 (최신 아키텍처)15
RAM4 GB16 GB 이상15
저장 공간64 GB SSD128 GB 이상 SSD15
네트워크1 Gbps1 Gbps 이상16
운영체제Ubuntu 20.04+Ubuntu 22.04+ LTS17

2.2 Ubuntu 환경에 필수 도구 설치

이 섹션에서는 Ubuntu 22.04 LTS를 기준으로 ERPNext 배포에 필요한 Docker, Docker Compose, Git을 설치하는 과정을 단계별로 안내한다.

2.2.1 Docker 및 Docker Compose 설치

최신 버전의 Docker와 안정성을 확보하기 위해 Docker의 공식 apt 리포지토리를 사용하는 것이 권장된다. 최신 Docker Engine 패키지에는 Docker Compose가 플러그인 형태로 포함되어 있어 별도로 설치할 필요가 없다.20

  1. 기존 버전 제거: 시스템에 이전 버전의 Docker가 설치되어 있다면 충돌을 방지하기 위해 먼저 제거한다.
sudo apt-get remove docker docker-engine docker.io containerd runc
  1. 리포지토리 설정: apt가 HTTPS를 통해 리포지토리를 사용할 수 있도록 필요한 패키지를 설치하고 Docker의 공식 GPG 키를 추가한다.
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
  1. Docker apt 리포지토리 추가: apt 소스 목록에 Docker 리포지토리를 추가한다.
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  1. Docker Engine 설치: 패키지 목록을 업데이트하고 Docker 관련 패키지들을 설치한다.
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

2.2.2 sudo 없이 Docker 명령어 사용 설정

매번 sudo를 입력하는 번거로움을 줄이고 스크립트 작성을 용이하게 하기 위해, 현재 사용자를 docker 그룹에 추가한다. 이 설정은 터미널을 재시작하거나 재로그인한 후에 적용된다.19

sudo usermod -aG docker $USER
newgrp docker

2.2.3 Git 설치

공식 frappe_docker 리포지토리를 로컬 시스템으로 복제(clone)하기 위해 Git을 설치한다.23

sudo apt-get update
sudo apt-get install -y git

2.3 데이터 영속성을 위한 볼륨 전략

Docker 컨테이너는 기본적으로 비영속적(non-persistent)이다. 즉, 컨테이너가 삭제되면 컨테이너 내부에 저장된 모든 데이터도 함께 사라진다. 따라서 데이터베이스 파일, 사용자가 업로드한 파일, 사이트 설정, 로그 등 영구적으로 보존해야 하는 데이터는 반드시 컨테이너 외부의 영속적인 스토리지에 저장해야 한다. Docker에서는 이를 위해 볼륨(Volume)을 사용한다.25

데이터 영속성 전략의 선택은 단순한 기술적 선호의 문제가 아니라, 시스템의 이식성, 관리 용이성, 그리고 호스트와의 결합도(coupling)를 결정하는 중요한 아키텍처적 결정이다. frappe_docker는 주로 두 가지 방식, 즉 명명된 볼륨(named volumes)과 바인드 마운트(bind mounts)를 지원한다.

  • 명명된 볼륨 (Named Volumes): frappe_docker의 기본 설정은 volumes: sites: {}와 같이 명명된 볼륨을 사용한다.11 이 방식은 Docker가 볼륨의 물리적 저장 위치를 전적으로 관리한다 ( /var/lib/docker/volumes/ 내). 가장 큰 장점은 이식성이다. docker-compose.yml 파일만 있으면 어떤 Docker 호스트에서든 동일한 구조를 정확하게 재현할 수 있어, 환경에 대한 의존성이 최소화된다. 초보자나 이식성을 최우선으로 하는 환경에 가장 적합하고 안전한 선택이다.

  • 바인드 마운트 (Bind Mounts): 시스템 관리자는 백업, 용량 모니터링, 외부 도구와의 연동 등을 위해 데이터의 물리적 위치를 직접 제어하기를 원할 수 있다. 이 경우, 호스트 시스템의 특정 디렉토리를 컨테이너의 디렉토리로 직접 연결하는 바인드 마운트를 사용한다.18

# 예: 호스트의 /mnt/data/sites 디렉토리를 생성
sudo mkdir -p /mnt/data/sites
# 컨테이너 내부 사용자와 권한을 맞추기 위해 소유권 변경 (frappe 사용자의 UID/GID는 보통 1000)
sudo chown -R 1000:1000 /mnt/data/sites

바인드 마운트는 관리의 편의성을 제공하지만, 호스트 파일 시스템과 컨테이너가 직접 연결되므로 호스트와 컨테이너 내부의 사용자 ID(UID) 및 그룹 ID(GID)가 일치하지 않을 경우 ‘Permission Denied’ 오류가 발생할 가능성이 높다.26 따라서 이 방식을 선택하는 것은 관리 편의성을 얻는 대신, 호스트 환경에 대한 의존성이 높아지고 잠재적인 권한 문제를 해결해야 하는 트레이드오프가 있음을 의미한다. 숙련된 관리자가 특정 경로에 데이터를 통합 관리해야 하는 프로덕션 환경에서는 권한 문제를 충분히 인지하고 해결할 수 있다는 전제 하에 바인드 마운트를 고려할 수 있다.

3. 기본 설치 및 구성: frappe_docker를 활용한 단계별 가이드

3.1 공식 frappe/frappe_docker 리포지토리 클론

모든 ERPNext Docker 배포 작업은 공식 GitHub 리포지토리를 로컬 머신에 복제하는 것에서 시작한다. 이 리포지토리에는 ERPNext 스택을 구성하고 실행하는 데 필요한 모든 파일과 스크립트가 포함되어 있다.24

git clone https://github.com/frappe/frappe_docker
cd frappe_docker

클론된 디렉토리의 주요 파일 및 디렉토리 구조는 다음과 같다:

  • compose.yaml: 모든 서비스(backend, frontend, scheduler 등)의 기본 정의를 포함하는 핵심 Compose 파일이다.

  • pwd.yml: ’Play with Docker’의 약자로, 로컬 개발 환경에서 빠르게 테스트할 수 있도록 미리 구성된 Compose 파일이다. 관리자 비밀번호(Administrator/admin)가 하드코딩되어 있어 프로덕션 환경에는 절대 사용해서는 안 된다.

  • overrides/: 특정 기능을 추가하거나 구성을 변경하기 위한 모듈식 YAML 파일들이 위치한 디렉토리다. 예를 들어, compose.mariadb.yaml은 MariaDB 데이터베이스 서비스를 추가하고, compose.traefik-ssl.yaml은 Traefik 리버스 프록시와 Let’s Encrypt를 통한 SSL/TLS 설정을 추가한다.

  • example.env: 프로덕션 환경 구성을 위한 환경 변수 템플릿 파일이다.

  • images/: 사용자 정의 앱을 포함하는 Docker 이미지를 빌드하기 위한 Containerfile(Dockerfile)과 관련 스크립트가 포함되어 있다.

3.2 개발 환경 빠른 시작 (pwd.yml)

가장 간단하게 로컬 개발 환경에서 ERPNext를 즉시 실행하고 테스트해볼 수 있는 방법은 pwd.yml 파일을 사용하는 것이다. 이 방법은 프로덕션 용도가 아니라는 점을 명확히 인지해야 한다.24

다음 명령어를 실행하면 필요한 모든 이미지를 다운로드하고 컨테이너를 백그라운드에서 실행한다.

docker compose -f pwd.yml up -d

컨테이너들이 시작된 후, create-site라는 일회성 컨테이너가 기본 사이트(frontend)를 생성한다. 이 과정이 완료될 때까지 수 분 정도 소요될 수 있다. 다음 명령어로 로그를 확인하여 사이트 생성이 완료되었는지 확인할 수 있다.24

docker compose -f pwd.yml logs -f create-site

로그 출력이 멈추고 컨테이너가 정상적으로 종료되면, 웹 브라우저에서 http://localhost:8080으로 접속한다. 기본 관리자 계정인 Administrator와 비밀번호 admin으로 로그인하여 ERPNext를 탐색할 수 있다.24

3.3 프로덕션 환경 구성 준비 (.env 파일)

안정적인 프로덕션 배포를 위해서는 pwd.yml 대신, 환경 변수를 통해 구성을 제어하는 방식을 사용해야 한다. 첫 단계는 example.env 파일을 .env 파일로 복사하여 자신만의 환경 설정을 구성하는 것이다.31

cp example.env.env

.env 파일을 열어 다음 핵심 변수들을 자신의 환경에 맞게 수정한다. 이 파일에 정의된 변수들은 docker compose 명령어 실행 시 자동으로 로드되어 Compose 파일 내의 ${VARIABLE_NAME} 플레이스홀더를 대체한다.

다음 표는 프로덕션 환경 구성 시 반드시 설정해야 하는 핵심 변수들을 정리한 것이다. 각 변수가 어떤 서비스나 구성 파일과 연관되는지 명시하여 설정 오류를 줄이고 이해를 돕는다.

변수명설명예시 값관련 오버라이드/참고
FRAPPE_VERSIONFrappe 프레임워크의 버전 태그.v15.0.0compose.yaml
ERPNEXT_VERSIONERPNext 애플리케이션의 버전 태그.v15.0.0compose.erpnext.yaml
SITES생성하고 서비스할 사이트의 FQDN. 백틱(`)으로 감싸야 함.`erp.mycompany.com`create-site 서비스, traefik-ssl.yaml
DB_PASSWORDMariaDB/PostgreSQL의 root 사용자 비밀번호.a-very-strong-passwordcompose.mariadb.yaml
ADMIN_PASSWORDERPNext 사이트의 Administrator 계정 비밀번호.another-strong-passwordcreate-site 서비스
LETSENCRYPT_EMAILLet’s Encrypt SSL 인증서 발급 및 갱신 알림용 이메일.admin@mycompany.comcompose.traefik-ssl.yaml
DB_HOST외부 데이터베이스 사용 시 호스트 주소.mariadb-databasecompose.yaml

3.4 오버라이드 파일을 이용한 프로덕션 스택 실행

frappe_docker의 오버라이드 파일 시스템은 ‘구성의 조합(Composition over Configuration)’ 원칙을 효과적으로 적용한 사례다. 이는 단일의 거대하고 복잡한 설정 파일을 유지보수하는 대신, 재사용 가능한 작은 기능 단위(DB, 프록시, 캐시 등)를 레고 블록처럼 조합하여 원하는 최종 시스템 상태를 만들어내는 유연하고 확장 가능한 접근 방식이다. 사용자는 다양한 데이터베이스(MariaDB, PostgreSQL), 프록시(Traefik, Nginx, 없음), 캐시(Redis) 조합을 원할 수 있다.11

compose.yaml에는 모든 서비스의 ’기본 뼈대’만 정의하고, overrides/ 디렉토리의 각 파일은 특정 기능을 추가하는 역할을 한다.

사용자는 Docker Compose의 -f 옵션을 사용하여 여러 YAML 파일을 계층적으로 적용할 수 있다. 명령어에 나열된 순서대로 파일이 로드되며, 뒤에 오는 파일의 내용이 앞선 파일의 내용을 덮어쓰거나 확장한다.11

예를 들어, MariaDB를 데이터베이스로, Redis를 캐시로, 그리고 Traefik을 통해 SSL/TLS를 활성화하는 일반적인 프로덕션 환경을 구성하는 명령어는 다음과 같다.31

docker compose \
--project-name erpnext-prod \
-f compose.yaml \
-f overrides/compose.erpnext.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.traefik-ssl.yaml \
up -d

이 명령어는 “기본 스택(compose.yaml)에 ERPNext 애플리케이션(compose.erpnext.yaml), MariaDB 서비스(compose.mariadb.yaml), Redis 서비스(compose.redis.yaml), 그리고 Traefik 기반 SSL 기능(compose.traefik-ssl.yaml)을 추가하여 erpnext-prod라는 이름으로 실행하라“는 의미를 가진다. 이 방식은 새로운 기능을 추가할 때(예: 새로운 캐시 시스템 지원) 기존 파일을 수정할 필요 없이 새로운 오버라이드 파일만 추가하면 되므로, 프로젝트의 장기적인 유지보수성을 크게 향상시킨다.

명령 실행 후, configurator 컨테이너가 먼저 실행되어 초기 설정을 완료하고, 그 후에 create-site 컨테이너가 .env 파일에 지정된 SITES 변수 값으로 사이트를 생성한다. 모든 과정이 완료되면 지정된 도메인으로 ERPNext에 접속할 수 있다.

4. 컨테이너 생명주기 관리 및 운영

Docker Compose로 배포된 ERPNext 스택을 효과적으로 운영하기 위해서는 컨테이너의 생명주기를 관리하는 핵심 명령어와 컨테이너 내부에서 ERPNext 애플리케이션을 제어하는 bench CLI의 활용법을 숙지해야 한다.

4.1 docker compose 핵심 명령어 활용

docker compose 명령어는 ERPNext 스택 전체를 하나의 단위로 관리할 수 있게 해준다.

  • up: docker-compose.yml 파일에 정의된 구성에 따라 컨테이너, 네트워크, 볼륨을 생성하고 시작한다. -d 또는 --detach 플래그를 함께 사용하면 컨테이너들을 백그라운드에서 실행하고 터미널을 즉시 반환받을 수 있다.11
docker compose -f... up -d
  • down: up으로 생성된 스택의 컨테이너와 네트워크를 중지하고 제거한다. 데이터의 영속성을 위해 명명된 볼륨은 기본적으로 삭제되지 않는다. 만약 볼륨까지 완전히 삭제하려면 --volumes 플래그를 추가해야 하며, 이 경우 모든 데이터가 유실되므로 극도의 주의가 필요하다.11
docker compose -f... down
  • ps: 현재 프로젝트에 속한 컨테이너들의 상태(Up, Exited, Paused 등)와 포트 매핑 정보를 보여준다. 시스템이 정상적으로 동작하는지 빠르게 확인할 때 유용하다.18
docker compose -f... ps
  • logs: 특정 서비스 또는 전체 스택의 로그를 출력한다. 문제 해결 시 가장 먼저 확인해야 할 정보다. -f 또는 --follow 플래그를 사용하면 로그를 실시간으로 스트리밍하여 확인할 수 있다.24
# backend 서비스의 로그만 실시간으로 확인
docker compose -f... logs -f backend
  • exec: 실행 중인 컨테이너 내부에서 특정 명령어를 실행한다. 이는 bench CLI 명령어를 실행하여 ERPNext 사이트를 관리하는 데 필수적인 통로 역할을 한다.19
# backend 컨테이너 내부에서 bash 쉘을 대화형으로 실행
docker compose -f... exec backend bash
  • pull: docker-compose.yml 파일에 정의된 모든 서비스의 이미지를 Docker Hub나 지정된 레지스트리에서 최신 버전으로 다운로드한다. 시스템 업데이트 시 컨테이너를 재생성하기 전에 이 명령어를 실행한다.36
docker compose -f... pull

4.2 프로젝트 이름(--project-name)을 통한 격리

Docker Compose는 기본적으로 현재 작업 디렉토리의 이름을 프로젝트 이름으로 사용하여 생성되는 모든 리소스(컨테이너, 네트워크, 볼륨)의 접두사로 사용한다. 이 때문에 동일한 서버에서 여러 개의 ERPNext 인스턴스(예: 프로덕션, 스테이징, 개발)를 운영하고자 할 때, 각 인스턴스를 별도의 디렉토리에서 실행해야 하는 제약이 생긴다.

--project-name 또는 -p 플래그는 이러한 제약을 해결한다. 이 플래그를 사용하여 각 스택에 고유한 프로젝트 이름을 명시적으로 지정하면, 동일한 디렉토리에서도 리소스 이름 충돌 없이 여러 인스턴스를 독립적으로 관리할 수 있다.11

# 'production-erp' 라는 이름으로 프로덕션 프로젝트 실행
docker compose -p production-erp -f... up -d

# 'staging-erp' 라는 이름으로 스테이징 프로젝트 실행
docker compose -p staging-erp -f... up -d

4.3 bench CLI: 컨테이너 내부의 ERPNext 제어 센터

bench는 Frappe 프레임워크와 ERPNext 사이트를 관리하기 위한 강력한 커맨드 라인 인터페이스(CLI) 도구다. Docker 환경에서는 docker compose exec 명령어를 통해 backend 컨테이너 내부에서 이 bench 명령어를 실행해야 한다. 이는 사이트 생성, 앱 설치, 데이터 마이그레이션 등 핵심적인 관리 작업의 기반이 된다.35

  • 새 사이트 추가: 기존 스택에 새로운 사이트를 추가할 때 사용한다.
docker compose exec backend bench new-site staging.mycompany.com --mariadb-root-password 'your-db-password' --admin-password 'your-admin-password'
  • 애플리케이션 설치: 특정 사이트에 새로운 앱(예: HRMS)을 설치한다.
docker compose exec backend bench --site staging.mycompany.com install-app hrms
  • 데이터베이스 마이그레이션: ERPNext 버전 업데이트 후, 데이터베이스 스키마를 최신 버전의 코드에 맞게 변경하기 위해 반드시 실행해야 한다.
docker compose exec backend bench --site erp.mycompany.com migrate
  • 캐시 클리어: 시스템 설정 변경이나 업데이트 후 발생할 수 있는 문제를 해결하기 위해 캐시를 삭제한다.
docker compose exec backend bench --site erp.mycompany.com clear-cache
  • 관리자 비밀번호 재설정: Administrator 계정의 비밀번호를 잊어버렸을 때 재설정한다.
docker compose exec backend bench --site erp.mycompany.com set-admin-password 'new-secure-password'

다음 표는 일상적인 운영 및 유지보수 작업에 필요한 명령어들을 빠르게 찾아볼 수 있는 참조 가이드 역할을 한다. Docker 레벨의 관리와 애플리케이션(bench) 레벨의 관리를 명확히 구분하여 보여줌으로써 혼동을 줄인다.

작업 목표Docker Compose 명령어Bench CLI 명령어 ( docker compose exec backend... )
전체 스택 시작docker compose up -d-
전체 스택 중지/제거docker compose down-
컨테이너 상태 확인docker compose ps-
실시간 로그 확인docker compose logs -f [service-name]-
새 사이트 생성-bench new-site [site-name] --mariadb-root-password...
사이트 데이터베이스 마이그레이션-bench --site [site-name] migrate
사이트 캐시 삭제-bench --site [site-name] clear-cache
특정 앱 설치-bench --site [site-name] install-app [app-name]
사이트 백업-bench --site [site-name] backup --with-files
사이트 복원-bench --site [site-name] restore [path/to/sql.gz] --with-public-files...

5. 심층 분석: compose.yaml 구조와 핵심 서비스

frappe_dockercompose.yaml 파일은 ERPNext를 구동하는 복잡한 시스템을 여러 개의 독립적인 서비스 컨테이너로 나누어 정의한 청사진이다. 각 서비스의 역할과 상호작용을 이해하는 것은 시스템을 안정적으로 운영하고 문제를 해결하는 데 필수적이다.

5.1 서비스 아키텍처 해부

frappe_docker의 다중 컨테이너 아키텍처는 각 컴포넌트가 마이크로서비스와 유사하게 독립적인 역할을 수행하며, 정의된 네트워크를 통해 통신하는 구조를 가진다.38

  • configurator: 이 서비스는 스택의 ’지휘자’와 같은 역할을 하는 일회성 컨테이너다. 다른 주요 서비스들이 시작되기 전에 먼저 실행되어, 데이터베이스 연결 확인, common_site_config.json 파일 생성 등 필수적인 초기 설정 작업을 수행한다. 작업이 성공적으로 완료되면 이 컨테이너는 종료되며, 다른 서비스들은 이 컨테이너의 성공적인 종료를 확인한 후에야 시작된다. 이는 복잡한 애플리케이션 스택의 시작 순서와 의존성을 관리하는 정교한 오케스트레이션 패턴이다.11

  • create-site: configurator와 마찬가지로 일회성 컨테이너이며, .env 파일에 정의된 SITES 변수를 기반으로 실제 ERPNext 사이트를 생성하고 필요한 앱을 설치하는 역할을 한다.

  • backend: Gunicorn WSGI 서버를 실행하여 Python으로 작성된 Frappe/ERPNext의 핵심 비즈니스 로직을 처리하는 주 애플리케이션 컨테이너다. 모든 동적 요청 처리와 bench CLI 실행이 이곳에서 이루어진다.19

  • frontend: Nginx를 리버스 프록시로 사용하여 외부의 HTTP/HTTPS 요청을 수신하는 진입점이다. 정적 자산(CSS, JavaScript, 이미지 파일 등)을 직접 서빙하여 backend의 부하를 줄이고, 동적 API 요청은 backend 컨테이너로 전달(proxy)하는 역할을 한다.18

  • db (MariaDB/PostgreSQL): 모든 데이터를 저장하는 데이터베이스 서버 컨테이너다. overrides 파일을 통해 MariaDB와 PostgreSQL 중에서 선택할 수 있다.11

  • redis-cache, redis-queue, redis-socketio: 시스템 성능과 반응성을 향상시키기 위해 세 개의 독립된 Redis 인스턴스가 사용된다. redis-cache는 자주 사용되는 데이터를 메모리에 저장하여 데이터베이스 조회를 줄이고, redis-queue는 이메일 발송과 같이 시간이 오래 걸리는 작업을 백그라운드에서 비동기적으로 처리하기 위한 작업 큐를 관리하며, redis-socketio는 실시간 알림과 같은 웹소켓 통신을 담당한다.11

  • scheduler: 정기적으로 실행되어야 하는 작업(예: 자동 백업, 요약 보고서 생성)을 관리하고 redis-queue에 작업을 등록하는 스케줄러 컨테이너다.

  • worker-short, worker-long: redis-queue에 등록된 백그라운드 작업을 실제로 처리하는 워커 컨테이너들이다. 작업의 예상 실행 시간에 따라 짧은 작업(worker-short)과 긴 작업(worker-long)을 처리하는 큐가 분리되어 있어, 긴 작업이 짧고 중요한 작업을 막는 현상을 방지한다.

5.2 depends_on을 통한 서비스 시작 순서 제어

depends_on 지시어는 서비스 간의 의존성을 정의하여 컨테이너의 시작 순서를 제어한다. 예를 들어, backend 서비스는 데이터베이스(db)와 Redis(redis-queue 등)가 먼저 시작되어야 정상적으로 동작할 수 있다. 하지만 단순히 컨테이너가 ’시작’된 것만으로는 서비스가 요청을 받을 ’준비’가 되었다고 보장할 수 없다. 데이터베이스는 초기화에 시간이 걸릴 수 있기 때문이다.

frappe_docker는 이 문제를 configurator 서비스와 condition: service_completed_successfully 옵션을 조합하여 해결한다.9 backend, frontend 등 대부분의 서비스는 configurator가 성공적으로 모든 초기화 작업을 마치고 정상 종료될 때까지 시작을 대기한다. 이 패턴은 Docker Compose의 내장 기능만으로는 부족한 애플리케이션 레벨의 의존성 관리를 사용자 정의 스크립트와 일회성 컨테이너를 조합하여 해결하는 영리한 방법으로, 복잡한 애플리케이션을 컨테이너화할 때 고려해야 할 중요한 아키텍처 패턴을 보여준다.

5.3 네트워크: 컨테이너 간의 안전한 통신

docker compose up 명령을 실행하면, Docker Compose는 기본적으로 프로젝트를 위한 격리된 브릿지 네트워크(예: erpnext-prod_default)를 생성한다. 이 네트워크에 속한 모든 컨테이너들은 방화벽 규칙을 별도로 설정할 필요 없이 서로 안전하게 통신할 수 있다.7

이때 각 컨테이너는 docker-compose.yml에 정의된 서비스 이름을 호스트 이름으로 사용하여 다른 서비스를 찾을 수 있다. 예를 들어, backend 컨테이너의 설정 파일에는 데이터베이스 호스트가 localhost나 IP 주소가 아닌 db로 지정되어 있다. Docker의 내장 DNS 서버가 db라는 호스트 이름을 db 서비스 컨테이너의 내부 IP 주소로 자동으로 해석해주기 때문이다. 이 방식은 컨테이너의 IP 주소가 변경되더라도 설정 파일을 수정할 필요가 없게 만들어 구성의 유연성을 높인다.

5.4 볼륨: 데이터의 영속성과 공유

volumes 섹션은 데이터의 영속성을 보장하는 핵심적인 부분이다. compose.yaml에 정의된 siteslogs 같은 명명된 볼륨은 컨테이너의 생명주기와 독립적으로 존재한다. 즉, docker compose down으로 컨테이너를 삭제하거나 docker compose up -d로 컨테이너를 새 버전으로 교체하더라도 볼륨에 저장된 데이터는 그대로 보존된다.9

또한, 하나의 볼륨을 여러 컨테이너가 공유하여 사용할 수 있다. 예를 들어, sites 볼륨은 사용자가 업로드한 파일과 사이트 설정이 저장되는 공간으로, backend(파일 업로드 처리), frontend(정적 파일 서빙), 그리고 백업 작업을 수행할 수 있는 worker 컨테이너들이 모두 이 볼륨에 접근해야 한다. compose.yaml 파일은 이 sites 볼륨을 필요한 모든 서비스에 마운트하도록 설정하여 일관된 데이터 접근을 보장한다.

6. 고급 활용: 사용자 정의 앱 통합 및 이미지 빌드

프로덕션 환경에서 ERPNext를 운영할 때는 Frappe나 ERPNext 커뮤니티에서 제공하는 서드파티 앱을 설치하거나, 기업의 고유한 비즈니스 로직을 담은 자체 앱을 개발하여 통합해야 하는 경우가 많다. 이때 공식 frappe/erpnext 이미지를 그대로 사용하는 대신, 필요한 모든 앱을 포함한 사용자 정의(Custom) Docker 이미지를 빌드하여 사용하는 것이 안정성과 배포 일관성을 위해 필수적이다.40

이러한 접근 방식은 ‘불변 인프라(Immutable Infrastructure)’ 원칙을 ERPNext 애플리케이션 레이어에 적용하는 것이다. 런타임 환경에서 상태를 변경(예: 컨테이너 실행 후 bench install-app 실행)하는 대신, 모든 종속성이 포함된 완전한 상태의 아티팩트(Docker 이미지)를 빌드 단계에서 미리 생성한다. 배포 시에는 이 불변의 아티팩트를 교체하기만 하면 된다. 이 모델은 배포의 예측 가능성과 신뢰성을 극대화하고, 문제 발생 시 이전 버전의 이미지로 간단하게 롤백할 수 있게 하여 CI/CD 파이프라인과 통합하기에 이상적이다.12

6.1 왜 사용자 정의 이미지가 필요한가?

실행 중인 컨테이너에 접속하여 bench get-appbench install-app 명령어로 앱을 설치하는 방식은 개발 환경에서는 편리할 수 있다. 하지만 이 ‘가변적인(mutable)’ 방식은 다음과 같은 문제점을 가진다:

  • 재현성 부족: 수동 작업이 개입되므로 배포마다 결과가 달라질 수 있으며, 새로운 서버에 동일한 환경을 구성하기 어렵다.

  • 배포 복잡성: 배포 프로세스에 컨테이너 시작 외에 추가적인 앱 설치 단계가 포함되어 자동화를 저해한다.

  • 확장성 문제: 오토스케일링 등으로 새로운 컨테이너 인스턴스가 생성될 때마다 매번 앱을 설치해야 하므로 비효율적이다.

사용자 정의 이미지를 빌드하면 이러한 문제들을 해결할 수 있다. 모든 앱과 종속성이 이미지 안에 ‘구워져(baked-in)’ 있으므로, 이 이미지를 실행하는 것만으로 완전한 애플리케이션 환경이 즉시 준비된다.

6.2 apps.json으로 앱 목록 정의하기

사용자 정의 이미지에 포함할 앱 목록은 apps.json이라는 파일을 통해 선언적으로 정의한다. 이 파일에는 설치할 각 앱의 Git 저장소 URL과 사용할 브랜치(또는 태그)를 JSON 배열 형식으로 명시한다.42

예를 들어, ERPNext와 HRMS 앱, 그리고 비공개(private) 저장소에 있는 커스텀 앱을 설치하려면 다음과 같이 apps.json 파일을 작성한다.

[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-15"
},
{
"url": "https://github.com/frappe/hrms",
"branch": "version-15"
},
{
"url": "https://<YOUR_GITHUB_USERNAME>:<YOUR_PERSONAL_ACCESS_TOKEN>@github.com/<YOUR_GITHUB_USERNAME>/<YOUR_CUSTOM_APP_REPO>.git",
"branch": "main"
}
]

비공개 저장소에 접근하기 위해서는 URL에 GitHub 사용자 이름과 인증을 위한 개인용 액세스 토큰(Personal Access Token)을 포함해야 한다.42

6.3 Containerfile을 이용한 이미지 빌드 프로세스

이미지 빌드 프로세스는 docker build 명령어를 통해 이루어진다. frappe_docker 리포지토리의 images/custom/Containerfile은 이 과정을 위한 템플릿 역할을 한다. 빌드 시 필요한 정보들(앱 목록, Frappe/ERPNext 버전 등)은 --build-arg 옵션을 통해 전달된다.

  1. apps.json 인코딩: apps.json 파일의 내용을 Base64로 인코딩하여 환경 변수로 설정한다. 이는 JSON 문자열이 쉘 환경에서 깨지지 않고 안전하게 전달되도록 하기 위함이다.12
export APPS_JSON_BASE64=$(base64 -w 0 /path/to/your/apps.json)
  1. 이미지 빌드: docker build 명령어를 실행하여 이미지를 빌드한다. --tag 옵션으로 이미지의 이름과 버전을 지정한다. 이 이름은 나중에 docker-compose.yml에서 사용된다.34
docker build \
--build-arg FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg FRAPPE_BRANCH=version-15 \
--build-arg PYTHON_VERSION=3.10.12 \
--build-arg NODE_VERSION=18.18.2 \
--build-arg APPS_JSON_BASE64=$APPS_JSON_BASE64 \
--tag my-registry/my-erpnext-custom:1.0.0 \
--file images/custom/Containerfile.

6.4 빌드된 이미지를 배포에 적용하기

사용자 정의 이미지 빌드가 완료되면, 이 이미지를 사용하도록 Docker Compose 설정을 수정해야 한다.

  1. compose.yaml 수정: frappe_docker 루트의 compose.yaml 파일을 직접 수정하거나, 별도의 오버라이드 파일(예: overrides/compose.custom.yaml)을 생성하는 것이 좋다. 파일 내에서 frappe/erpnext 이미지를 사용하는 모든 서비스(예: backend, frontend, configurator, worker-* 등)의 image 지시어를 방금 빌드한 이미지 이름(my-registry/my-erpnext-custom:1.0.0)으로 변경한다.
#...
x-customizable-image: &customizable_image
image: my-registry/my-erpnext-custom:1.0.0
pull_policy: if_not_present
#...

pull_policy: if_not_present (또는 never)를 추가하면, Docker가 원격 레지스트리에서 이미지를 찾기 전에 로컬에 빌드된 이미지를 먼저 사용하도록 강제할 수 있다.34

  1. 스택 재시작: 수정된 구성 파일을 사용하여 스택을 다시 시작한다. Docker Compose는 이미지 이름이 변경된 것을 감지하고, 기존 컨테이너를 삭제한 뒤 새로운 사용자 정의 이미지를 기반으로 컨테이너를 다시 생성한다.
docker compose -f compose.yaml -f... up -d
  1. 앱 설치 확인: 사이트 생성 시 bench new-site 명령어에 --install-app [custom-app-name] 옵션을 추가하거나, 이미 생성된 사이트의 경우 bench --site [site-name] install-app [custom-app-name] 명령어를 실행하여 커스텀 앱을 활성화한다.

7. 데이터 관리: 백업, 복원 및 업데이트 전략

안정적인 ERP 시스템 운영의 핵심은 데이터의 무결성을 보장하고, 재해 발생 시 신속하게 복구하며, 시스템을 최신 상태로 안전하게 유지하는 것이다. Docker 환경에서의 ERPNext는 bench CLI와 Docker 볼륨을 조합하여 체계적인 데이터 관리 전략을 제공한다.

7.1 백업 전략: bench와 볼륨

ERPNext 데이터 백업은 두 가지 레벨에서 접근할 수 있다: 애플리케이션 레벨과 볼륨 레벨. 대부분의 경우, 애플리케이션 레벨 백업이 권장된다.

7.1.1 애플리케이션 레벨 백업 (bench backup)

bench backup 명령어는 데이터베이스와 파일 시스템의 일관성이 보장된 상태의 스냅샷을 생성하는 가장 안정적이고 권장되는 방법이다. 이 명령어는 다음 세 가지 핵심 요소를 백업한다 45:

  • 데이터베이스 덤프 (.sql.gz): 사이트의 모든 데이터를 포함하는 압축된 SQL 파일.

  • 공개 파일 (public/files): 사용자가 업로드한 이미지, 첨부 파일 등 공개적으로 접근 가능한 파일들의 아카이브.

  • 비공개 파일 (private/files): 민감한 문서나 내부용 파일들의 아카이브.

  1. 백업 실행: docker compose exec를 통해 backend 컨테이너에서 bench backup 명령어를 실행한다. --with-files 플래그는 데이터베이스와 함께 파일들도 백업하도록 지시한다.
docker compose exec backend bench --site [your-site-name] backup --with-files
  1. 백업 파일 확인 및 복사: 백업 파일은 컨테이너 내부의 sites/[your-site-name]/private/backups/ 디렉토리에 타임스탬프가 찍힌 이름으로 생성된다. 이 파일들을 안전한 외부 저장소(예: S3, NAS)로 옮기기 위해 docker cp 명령어를 사용하여 호스트 머신으로 복사한다.47
# 컨테이너의 정확한 이름 확인 (예: erpnext-prod-backend-1)
docker compose -p erpnext-prod ps

# 백업 디렉토리 전체를 호스트의 /opt/erpnext_backups 로 복사
docker cp erpnext-prod-backend-1:/home/frappe/frappe-bench/sites/[your-site-name]/private/backups /opt/erpnext_backups/

7.1.2 볼륨 레벨 백업

Docker 볼륨 자체를 직접 백업하는 방법도 있다. 이는 전체 시스템 상태를 이미지처럼 보관하여 재해 복구 시나리오에 유용하다. 데이터 일관성을 보장하기 위해, 백업을 진행하는 동안에는 애플리케이션 컨테이너를 잠시 중지하는 것이 가장 안전하다.49

# 1. ERPNext 스택 중지
docker compose -p erpnext-prod down

# 2. 볼륨 데이터가 저장된 호스트 경로를 찾아 아카이빙
# (볼륨 경로 확인: docker volume inspect erpnext-prod_sites)
sudo tar -czf /opt/erpnext_backups/sites_volume_backup.tar.gz /var/lib/docker/volumes/erpnext-prod_sites/_data

# 3. 스택 다시 시작
docker compose -p erpnext-prod up -d

7.2 복원 절차: 재해로부터의 회복

bench backup으로 생성된 파일을 사용하여 사이트를 복원하는 절차는 백업의 역순으로 진행된다.

  1. 백업 파일 준비: 복원할 데이터베이스(.sql.gz), 공개 파일(.tar), 비공개 파일(.tar)을 준비한다.

  2. 컨테이너로 파일 복사: docker cp를 사용하여 백업 파일들을 실행 중인 backend 컨테이너 내부의 임시 경로(예: /tmp)로 복사한다.51

docker cp /opt/erpnext_backups/20250625_...-database.sql.gz erpnext-prod-backend-1:/tmp/
docker cp /opt/erpnext_backups/20250625_...-files.tar erpnext-prod-backend-1:/tmp/
docker cp /opt/erpnext_backups/20250625_...-private-files.tar erpnext-prod-backend-1:/tmp/
  1. 복원 실행: backend 컨테이너에 접속하여 bench restore 명령어를 실행한다. --force 플래그는 기존 사이트 데이터를 덮어쓸 때 필요할 수 있다.47
docker compose exec backend bash
# --- 컨테이너 내부 ---
bench --site [your-site-name] restore /tmp/20250625_...-database.sql.gz \
--with-public-files /tmp/20250625_...-files.tar \
--with-private-files /tmp/20250625_...-private-files.tar \
--force
  1. 마이그레이션: 복원된 데이터베이스 스키마가 현재 코드 버전과 다를 수 있으므로, bench migrate를 실행하여 스키마를 최신 상태로 맞춘다.51
bench --site [your-site-name] migrate
exit

7.3 안전한 업데이트 절차

Docker 기반 ERPNext의 업데이트는 기존 서버의 파일을 직접 수정하는 ’인플레이스 업그레이드(In-place Upgrade)’가 아닌, 데이터(볼륨)는 그대로 유지한 채 애플리케이션 실행 환경(컨테이너) 전체를 새로운 버전으로 교체하는 ‘재생성 및 교체(Recreate and Replace)’ 모델을 따른다. 이 모델은 업데이트 과정의 위험을 크게 줄여주며, 문제 발생 시 이전 버전 이미지로 간단하게 롤백할 수 있는 장점이 있다.

다음은 안전한 업데이트를 위한 단계별 절차다 36:

  1. 백업: 업데이트를 시작하기 전, 만일의 사태에 대비하여 반드시 7.1절의 방법으로 전체 사이트를 백업한다.

  2. 리포지토리 업데이트: 로컬 frappe_docker 디렉토리에서 git pull을 실행하여 최신 Compose 파일과 스크립트를 가져온다.

cd /path/to/frappe_docker
git pull origin main
  1. 버전 변경: .env 파일을 열어 FRAPPE_VERSIONERPNEXT_VERSION을 업데이트하려는 새 버전 태그로 수정한다. (만약 사용자 정의 이미지를 사용 중이라면, 새 버전을 기반으로 6.3절의 절차에 따라 새 이미지를 빌드해야 한다.)

  2. 새 이미지 다운로드: docker compose pull 명령어로 .env 파일에 명시된 새 버전의 이미지들을 원격 레지스트리에서 다운로드한다.

docker compose -p erpnext-prod -f... pull
  1. 컨테이너 재생성: docker compose up -d를 다시 실행한다. Docker Compose는 구성 파일의 이미지 태그가 변경된 것을 감지하고, 해당 서비스의 기존 컨테이너를 중지 및 제거한 후 새 이미지 기반의 컨테이너로 교체하여 다시 생성한다. 데이터 볼륨은 그대로 유지되어 새 컨테이너에 다시 연결(re-attach)된다.10
docker compose -p erpnext-prod -f... up -d
  1. 데이터베이스 마이그레이션: 새 버전의 애플리케이션 코드가 요구하는 데이터베이스 스키마 변경 사항을 적용하기 위해 bench migrate를 실행한다. 이는 업데이트 과정에서 가장 중요한 단계 중 하나다.
docker compose -p erpnext-prod exec backend bench --site [your-site-name] migrate
  1. 캐시 클리어: 업데이트 후 발생할 수 있는 잠재적인 프론트엔드 문제를 방지하기 위해 캐시를 삭제한다. 특히 CSS/JS 파일이 깨져 보일 경우 이 단계가 도움이 될 수 있다.55
docker compose -p erpnext-prod exec backend bench --site [your-site-name] clear-cache
docker compose -p erpnext-prod exec backend bench --site [your-site-name] clear-website-cache

8. 프로덕션 환경 구성: Traefik을 이용한 SSL/TLS 설정

인터넷에 노출되는 프로덕션 환경에서는 모든 통신을 암호화하기 위해 SSL/TLS를 적용하는 것이 필수적이다. 이를 위해 일반적으로 리버스 프록시(Reverse Proxy)를 사용하며, frappe_docker는 Traefik을 이용한 자동화된 SSL/TLS 설정을 강력하게 지원한다.

8.1 리버스 프록시와 Traefik의 역할

리버스 프록시는 외부 클라이언트의 요청을 받아 내부 네트워크의 적절한 서버로 전달해주는 중간 서버다. ERPNext 환경에서 리버스 프록시는 다음과 같은 중요한 역할을 수행한다 56:

  • SSL/TLS 종료(Termination): 클라이언트와의 HTTPS 통신을 처리하고 암호화/복호화 작업을 전담한다. 내부 애플리케이션 서버는 암호화 부담 없이 HTTP로만 통신할 수 있다.

  • 단일 진입점(Single Entry Point): 여러 웹 서비스가 있더라도 외부에는 80(HTTP), 443(HTTPS) 포트만 노출하여 보안을 강화한다.

  • 로드 밸런싱: 여러 backend 컨테이너 인스턴스로 트래픽을 분산시켜 가용성과 성능을 높일 수 있다.

Traefik은 클라우드 네이티브 환경을 위해 설계된 현대적인 리버스 프록시로, 특히 Docker와 긴밀하게 통합된다. 가장 큰 장점은 ‘자동 서비스 검색(Automatic Service Discovery)’ 기능이다. Traefik은 Docker 소켓을 모니터링하다가 새로운 컨테이너가 특정 라벨(label)을 가지고 시작되면, 해당 컨테이너로 트래픽을 라우팅하는 규칙을 자동으로 생성하고 적용한다. 이는 설정 파일을 수동으로 수정하고 서비스를 재시작해야 하는 전통적인 방식에 비해 훨씬 동적이고 편리하다.56

이러한 접근 방식은 인프라(보안, 라우팅)와 애플리케이션(비즈니스 로직)의 관심사를 명확하게 분리한다. ERPNext 애플리케이션 컨테이너는 SSL/TLS에 대해 전혀 알 필요 없이 내부 HTTP 통신에만 집중하고, 모든 외부 보안 통신은 전문화된 에지 라우터(Traefik)가 전담한다. 이 역할 분담은 각 구성 요소의 복잡성을 줄여 전체 시스템의 안정성과 관리 용이성을 높인다.

8.2 Traefik 및 SSL 활성화

frappe_docker 리포지토리는 Traefik 설정을 위한 오버라이드 파일을 미리 제공하여 SSL 활성화를 매우 간단하게 만든다.

  1. 전제 조건 확인: .env 파일에 다음 두 변수가 정확하게 설정되어 있는지 확인한다. 이는 Let’s Encrypt를 통한 자동 인증서 발급의 핵심 전제 조건이다.34
  • SITES: SSL 인증서를 발급받을 정확한 도메인 이름(예: `erp.mycompany.com`).

  • LETSENCRYPT_EMAIL: 인증서 만료 알림 등을 수신할 유효한 이메일 주소.

  1. Traefik 스택 실행: Traefik은 ERPNext 스택과 별개의 프로젝트로 관리하는 것이 일반적이다. 이를 통해 Traefik을 다른 서비스의 리버스 프록시로도 활용할 수 있다. overrides/compose.traefik.yaml은 Traefik 서비스 자체를, overrides/compose.traefik-ssl.yaml은 Let’s Encrypt를 이용한 SSL 설정을 정의한다.34
# Traefik 대시보드 도메인 및 관리자 이메일 설정
mkdir -p ~/gitops
echo 'TRAEFIK_DOMAIN=traefik.mycompany.com' > ~/gitops/traefik.env
echo 'EMAIL=admin@mycompany.com' >> ~/gitops/traefik.env

# Traefik 컨테이너 실행
docker compose --project-name traefik \
--env-file ~/gitops/traefik.env \
-f overrides/compose.traefik.yaml \
-f overrides/compose.traefik-ssl.yaml up -d
  1. ERPNext 스택과 Traefik 연동: ERPNext 스택을 시작할 때 overrides/compose.traefik-ssl.yaml을 포함시키면, frontend 서비스의 labels 섹션에 Traefik이 인식할 수 있는 라우팅 규칙이 자동으로 추가된다.
docker compose -p erpnext-prod \
-f compose.yaml \
-f overrides/compose.erpnext.yaml \
...
-f overrides/compose.traefik-ssl.yaml \
up -d

8.3 DNS 설정 및 연결 확인

  1. DNS A 레코드 설정: 도메인 등록 기관의 DNS 관리 페이지에서 .env 파일의 SITES에 설정한 도메인(예: erp.mycompany.com)과 Traefik 대시보드 도메인(예: traefik.mycompany.com)에 대한 A 레코드가 서버의 공인 IP 주소를 가리키도록 설정해야 한다.15

  2. 연결 확인: DNS 설정이 전파된 후(수 분에서 수 시간 소요될 수 있음), 웹 브라우저에서 https://erp.mycompany.com으로 접속한다. Traefik이 Let’s Encrypt로부터 SSL 인증서를 성공적으로 발급받았다면, 자물쇠 아이콘과 함께 안전한 HTTPS 연결이 수립된 ERPNext 로그인 페이지가 나타난다. Traefik은 frontend 서비스의 라벨을 동적으로 감지하여 해당 도메인으로 들어오는 HTTPS 트래픽을 frontend 컨테이너로 자동으로 라우팅하고, 발급받은 SSL 인증서를 적용하며, 주기적으로 갱신까지 관리한다.

9. 문제 해결: 일반적인 오류 및 해결 방안

Docker 기반으로 ERPNext를 운영하다 보면 다양한 문제에 직면할 수 있다. 이 섹션에서는 가장 빈번하게 발생하는 오류들의 원인을 진단하고 해결하는 체계적인 방법을 제시한다.

9.1 Error... Bind for 0.0.0.0:8080 failed: port is already allocated

  • 원인: 이 오류는 Docker가 컨테이너의 포트를 호스트 머신의 포트에 매핑하려고 할 때, 해당 호스트 포트(이 경우 8080)를 다른 프로세스나 이미 실행 중인 다른 컨테이너가 점유하고 있을 때 발생한다.58

  • 진단: 리눅스 환경에서 netstat 또는 lsof 명령어를 사용하여 특정 포트를 사용 중인 프로세스를 확인할 수 있다.60

sudo netstat -tulpn | grep :8080
# 또는
sudo lsof -i :8080
  • 해결책:
  1. 충돌 프로세스 종료: 진단 명령어로 확인된 프로세스가 불필요한 것이라면 해당 프로세스를 종료한다.

  2. 포트 변경: 점유 중인 프로세스를 중지할 수 없는 경우, pwd.yml 또는 사용하는 docker-compose.yml 파일에서 frontend 서비스의 ports 섹션을 수정하여 다른 호스트 포트를 사용하도록 변경한다. 예를 들어, 8081:80으로 변경하면 호스트의 8081 포트를 통해 접속할 수 있다.59

  3. 이전 컨테이너 정리: 이전에 실행했던 ERPNext 컨테이너가 제대로 종료되지 않고 남아있는 경우가 원인일 수 있다. docker compose down 명령어로 관련 스택을 완전히 정리한 후 다시 시도한다.61

9.2 502 Bad Gateway

  • 원인: 이 오류는 리버스 프록시 역할을 하는 frontend(Nginx) 컨테이너가 업스트림 서비스인 backend(Gunicorn) 컨테이너와 통신하지 못할 때 발생한다. 주로 backend 컨테이너가 시작되지 않았거나, 시작 중 오류로 인해 충돌했거나, 과도한 부하로 인해 응답하지 못하는 상태일 때 나타난다.62

  • 진단:

  1. docker compose ps 명령어로 backend를 포함한 모든 컨테이너가 Up 또는 healthy 상태인지 확인한다. Exited 상태라면 문제가 발생한 것이다.

  2. docker compose logs backend 명령어를 실행하여 backend 컨테이너의 로그를 상세히 확인한다. 데이터베이스 연결 실패, Python 의존성 문제, 설정 오류 등 구체적인 에러 메시지를 찾을 수 있다.

  3. docker compose logs frontend 명령어로 Nginx 로그를 확인하여 “upstream prematurely closed connection” 또는 “connect() failed“와 같은 메시지가 있는지 살펴본다.

  • 해결책:
  1. 컨테이너 재시작: 가장 간단한 해결책은 backend 컨테이너를 재시작하는 것이다: docker compose restart backend.

  2. 로그 분석 및 근본 원인 해결: backend 로그에서 발견된 오류를 해결한다. 예를 들어, 데이터베이스 비밀번호가 틀렸다면 .env 파일을 수정하고 스택을 재시작해야 한다.

  3. 리소스 확인: 서버의 CPU, 메모리(RAM) 자원이 부족하여 backend 프로세스가 비정상적으로 종료되는 경우도 있다. htop이나 docker stats와 같은 명령어로 시스템 리소스 사용량을 확인한다.62

9.3 Permission Denied on Volumes

  • 원인: 이 문제는 주로 바인드 마운트(bind mount)를 사용할 때 발생한다. 호스트 머신의 디렉토리 소유권/권한과 컨테이너 내부에서 프로세스를 실행하는 사용자의 UID(User ID)/GID(Group ID)가 일치하지 않기 때문이다. Frappe 컨테이너는 기본적으로 frappe라는 사용자(일반적으로 UID 1000, GID 1000) 권한으로 실행된다. 만약 호스트의 마운트된 디렉토리가 root 소유라면, frappe 사용자는 이 디렉토리에 파일을 쓰거나 읽을 수 없다.26

  • 진단: ls -ln /path/to/host/dir 명령어를 사용하여 호스트 디렉토리의 소유자 UID와 GID를 숫자로 확인한다.

  • 해결책:

  1. 권장 - 소유권 변경: 호스트 디렉토리의 소유권을 컨테이너 내부 사용자의 UID/GID와 일치시키는 것이 가장 안전하고 올바른 방법이다.
sudo chown -R 1000:1000 /path/to/your/erpnext/sites
  1. 임시 방편(비보안): 개발 환경에서 빠른 해결을 위해 디렉토리 권한을 모든 사용자가 읽고 쓸 수 있도록 변경할 수 있다. 이는 보안상 취약하므로 프로덕션 환경에서는 절대 사용해서는 안 된다.
sudo chmod -R 777 /path/to/your/erpnext/sites
  1. 컨테이너 실행 사용자 지정: docker-compose.yml 파일에서 서비스 정의에 user: "$(id -u):$(id -g)" 옵션을 추가하여, 컨테이너가 현재 호스트 사용자의 권한으로 실행되도록 할 수 있다. 이는 호스트와 컨테이너 간의 권한을 동기화하는 효과적인 방법이다.26

9.4 기타 GitHub 이슈 기반 문제들

  • 업데이트 후 자산(Assets) 문제: 시스템 업데이트 후 웹사이트의 CSS나 JavaScript가 깨져 보이는 경우가 있다. 이는 브라우저나 Redis 캐시에 이전 버전의 자산 정보가 남아있기 때문일 수 있다. bench clear-cachebench clear-website-cache를 실행하여 해결을 시도할 수 있다. 문제가 지속될 경우, Redis 캐시를 직접 비우는(FLUSHALL) 것이 필요할 수 있다.55

  • Windows/macOS에서의 문제: Windows의 NTFS 파일 시스템 권한 처리 방식이나 Docker Desktop의 리소스 제한 등으로 인해 예기치 않은 오류가 발생할 수 있다. 특히 파일 권한 문제나 빌드 스크립트의 줄바꿈 문자(CRLF vs LF) 차이로 문제가 발생하기도 한다.66 가능한 한 Linux 환경이나 Windows의 WSL2(Windows Subsystem for Linux 2) 환경에서 작업하는 것이 권장된다.

다음 표는 사용자가 현장에서 빈번하게 마주치는 문제들에 대해 원인-진단-해결의 명확한 프레임워크를 제공하여 문제 해결 시간을 단축시킨다.

오류 메시지주요 원인진단 명령어해결 방안
Port is already allocated호스트 포트 충돌sudo netstat -tulpn \vert grep [포트번호]포트 변경, 충돌 프로세스 종료, docker compose down
502 Bad Gatewayfrontendbackend 통신 실패docker compose ps, docker compose logs backendbackend 컨테이너 재시작, 로그 분석 후 원인 해결
Permission Denied (볼륨)호스트-컨테이너 간 UID/GID 불일치ls -ln [호스트 경로]sudo chown -R 1000:1000 [호스트 경로]
업데이트 후 UI 깨짐오래된 정적 자산 캐시docker compose logs frontend (404 에러 확인)exec backend bench clear-cache, exec backend bench clear-website-cache

10. 결론: 지속 가능한 ERPNext 운영을 위한 제언

10.1 보고서 요약

본 보고서는 Docker Compose를 사용하여 ERPNext를 배포하고 운영하는 전 과정을 심층적으로 다루었다. 시스템 요구사항 분석과 환경 준비부터 시작하여, 개발 및 프로덕션 환경을 위한 단계별 설치 절차, 컨테이너 생명주기 관리, 그리고 bench CLI를 활용한 애플리케이션 운영 방법을 상세히 설명했다. 또한, compose.yaml 파일의 아키텍처 분석을 통해 각 서비스의 역할과 상호작용 원리를 파악했으며, 사용자 정의 앱 통합을 위한 이미지 빌드, 데이터 백업 및 복원, 안전한 업데이트 절차와 같은 고급 활용 방안을 제시했다. 마지막으로, Traefik을 이용한 자동화된 SSL/TLS 설정과 현장에서 자주 발생하는 주요 오류에 대한 체계적인 문제 해결 가이드를 제공했다.

결론적으로, Docker Compose를 활용한 ERPNext 배포 방식은 전통적인 스크립트 기반 설치의 한계인 환경 의존성과 재현성 문제를 해결하고, 인프라를 코드로 관리(IaC)하는 현대적인 DevOps 관행에 부합하는 가장 효율적이고 안정적인 선택이다.

10.2 프로덕션 운영을 위한 추가 고려사항

본 보고서에서 다룬 내용을 바탕으로 안정적인 프로덕션 시스템을 구축할 수 있지만, 장기적이고 지속 가능한 운영을 위해서는 다음과 같은 추가적인 사항들을 고려해야 한다.

  • 보안 강화: .env 파일에는 데이터베이스 비밀번호와 같은 민감한 정보가 포함된다. 이 파일을 버전 관리 시스템(Git)에 절대 커밋해서는 안 되며, 접근 권한을 최소화해야 한다. 더 나아가, 프로덕션 환경에서는 Docker Secrets나 HashiCorp Vault와 같은 전문적인 비밀 관리 도구를 도입하여 민감한 정보를 안전하게 주입하는 방식을 고려하는 것이 좋다.

  • 모니터링 및 로깅: 시스템의 상태를 지속적으로 관찰하고 문제가 발생했을 때 신속하게 대응하기 위해서는 강력한 모니터링 및 로깅 시스템이 필수적이다. Prometheus와 Grafana를 연동하여 컨테이너의 CPU, 메모리, 네트워크 사용량과 같은 시스템 메트릭과 애플리케이션의 성능 지표(APM)를 시각화할 수 있다. 또한, ELK Stack(Elasticsearch, Logstash, Kibana)이나 Fluentd를 사용하여 여러 컨테이너에서 발생하는 로그를 중앙 집중식으로 수집하고 분석하는 체계를 구축해야 한다.

  • 성능 최적화: 비즈니스 규모가 성장함에 따라 시스템에 가해지는 부하도 증가한다. docker-compose.yml 파일에서 worker-shortworker-long 컨테이너의 복제본(replicas) 수를 조절하여 백그라운드 작업 처리 용량을 확장할 수 있다. 또한, MariaDB의 my.cnf 설정이나 Nginx의 워커 프로세스 수를 튜닝하여 특정 워크로드에 맞게 성능을 최적화하는 작업이 필요할 수 있다.

10.3 커뮤니티와 지속적인 학습

ERPNext와 Frappe 프레임워크는 활발한 오픈소스 커뮤니티에 의해 빠르게 발전하고 있다. 성공적인 시스템 운영을 위해서는 이러한 변화에 지속적으로 적응하고 학습하는 자세가 중요하다.

  • 공식 문서 및 포럼: Frappe/ERPNext 공식 문서와 토론 포럼(discuss.frappe.io)은 가장 신뢰할 수 있는 정보의 원천이다. 새로운 기능, 변경 사항, 모범 사례에 대한 정보를 얻고, 다른 사용자들이 겪는 문제와 해결책을 공유하며 커뮤니티의 집단 지성을 활용해야 한다.

  • GitHub 리포지토리: frappe/frappe_docker GitHub 리포지토리의 이슈(Issues) 페이지는 현재 알려진 버그나 개선 사항을 파악하고, 특정 문제에 대한 해결책을 찾는 데 매우 유용하다. 직접 문제를 보고하거나 해결책을 제안하며 프로젝트에 기여하는 것도 좋은 방법이다.

오픈소스 프로젝트의 진정한 가치는 코드 자체뿐만 아니라, 그 코드를 중심으로 형성된 커뮤니티와의 상호작용을 통해 발현된다. 지속적인 학습과 적극적인 커뮤니티 참여는 복잡한 ERP 시스템을 성공적으로 운영하고 비즈니스의 성장을 지원하는 가장 확실한 동력이 될 것이다.

11. 참고 자료

  1. Introduction - Documentation for Frappe Apps, https://docs.frappe.io/erpnext/user/manual/en/introduction
  2. What is erpnext: features, benefits & how it transforms Biz | Digiqt Blog, https://digiqt.com/blog/what-is-erpnext/
  3. en.wikipedia.org, https://en.wikipedia.org/wiki/ERPNext
  4. What is ERPNext and Why It Is the Best ERP for Manufacturing Industries?, https://www.dexciss.io/blog/educational-6/what-is-erpnext-and-why-it-is-the-best-erp-for-manufacturing-industries-37
  5. docs.docker.com, https://docs.docker.com/compose/#:~:text=Docker%20Compose%20is%20a%20tool,efficient%20development%20and%20deployment%20experience.
  6. Docker Compose - Docker Docs, https://docs.docker.com/compose/
  7. Docker Compose - What is It, Example & Tutorial - Spacelift, https://spacelift.io/blog/docker-compose
  8. How To Choose Between Docker Compose vs Docker? - CyberPanel, https://cyberpanel.net/blog/docker-compose-vs-docker
  9. Docker Compose - GeeksforGeeks, https://www.geeksforgeeks.org/devops/docker-compose/
  10. Benefits of compose over regular? : r/docker - Reddit, https://www.reddit.com/r/docker/comments/io2xpx/benefits_of_compose_over_regular/
  11. Containerizing ERPNext with Docker Compose - Aryan Mann, https://aryanmann.com/posts/erpnext/i
  12. Dockerize Custom Application in Frappe Framework: Migration from VM to Container, https://medium.com/@yashwanthtss7/dockerize-custom-application-in-frappe-framework-migration-from-vm-to-container-bac073ec1040
  13. How to Install ERPNext on Windows 11 Using Docker - Step-by-Step - Infintrix Technologies, https://infintrixtech.com/blog/install-erpnext-windows11-docker
  14. Why you should use Docker Compose | EDB - EnterpriseDB, https://www.enterprisedb.com/postgres-tutorials/why-you-should-use-docker-compose
  15. ERPNext - Install - Habitats Open Tech, https://portal.habitats.tech/ERPNext/ERPNext+-+Install
  16. How to setup & self-host ERPNext in Docker without Treafik (NGINX only) - Piotr Krzyzek, https://piotrkrzyzek.com/how-to-setup-self-host-erpnext-in-docker-without-treafik-nginx-only/
  17. [Tutorial][Docker] Installing ERPNext-15 on Docker: A Step-by-Step Guide [Production], https://discuss.frappe.io/t/tutorial-docker-installing-erpnext-15-on-docker-a-step-by-step-guide-production/111731
  18. A Step-by-Step Guide to Running Frappe ERPNext System in Docker Containers, https://frappedevops.hashnode.dev/a-step-by-step-guide-to-running-frappe-erpnext-system-in-docker-containers
  19. How To Run ERPNext ERP System in Docker Containers | ComputingForGeeks, https://computingforgeeks.com/run-erpnext-erp-system-in-docker-containers/
  20. Install Docker Engine on Ubuntu, https://docs.docker.com/engine/install/ubuntu/
  21. ERPNext Deployment - Blog of Hasitha Suneth, https://blog.hasithasuneth.com/docs/deployments/erpnext/
  22. Installing Docker on Ubuntu - Discover gists · GitHub, https://gist.github.com/subfuzion/90e8498a26c206ae393b66804c032b79
  23. Install Git In Docker - Medium, https://medium.com/@amreshy29/install-git-in-docker-10c07bad776d
  24. frappe/frappe_docker: Docker images for production and development setups of the Frappe framework and ERPNext - GitHub, https://github.com/frappe/frappe_docker
  25. Volumes - Docker Docs, https://docs.docker.com/engine/storage/volumes/
  26. How to resolve ‘permission denied’ error when mounting volume in Docker - LabEx, https://labex.io/tutorials/docker-how-to-resolve-permission-denied-error-when-mounting-volume-in-docker-417724
  27. Fix Permission Denied Errors When Mounting Volumes - Magetop, https://www.magetop.com/blog/how-to-fix-permission-denied-errors-when-mounting-volumes-in-docker/
  28. frappe/frappe: Low code web framework for real world applications, in Python and Javascript, https://github.com/frappe/frappe
  29. frappe/erpnext: Free and Open Source Enterprise Resource Planning (ERP) - GitHub, https://github.com/frappe/erpnext
  30. Can someone help me with erpnext on docker? : r/selfhosted - Reddit, https://www.reddit.com/r/selfhosted/comments/11e7aqq/can_someone_help_me_with_erpnext_on_docker/
  31. How can I get ERPNext running under docker for a quick look? - Frappe Forum, https://discuss.frappe.io/t/how-can-i-get-erpnext-running-under-docker-for-a-quick-look/90288
  32. docs/environment-variables.md · main · Christopher McKay / frappe_docker - GitLab, https://dev.egov.gy/christopher.mckay/frappe_docker/-/blob/main/docs/environment-variables.md
  33. docs/setup-options.md · main · Christopher McKay / frappe_docker - GitLab, https://dev.egov.gy/christopher.mckay/frappe_docker/-/blob/main/docs/setup-options.md
  34. [Guide] ERPNext-14 Production Setup (Docker) - Frappe Forum, https://discuss.frappe.io/t/guide-erpnext-14-production-setup-docker/105174
  35. docs/site-operations.md · v14.x · Christopher McKay / frappe_docker - GitLab, https://dev.egov.gy/christopher.mckay/frappe_docker/-/blob/v14.x/docs/site-operations.md
  36. How to update Docker version of ERPNext - Frappe Forum, https://discuss.frappe.io/t/how-to-update-docker-version-of-erpnext/74521
  37. docs/site-operations.md · main · Christopher McKay / frappe_docker - GitLab, https://dev.egov.gy/christopher.mckay/frappe_docker/-/blob/main/docs/site-operations.md
  38. ERPNext docker discussion - Deployment - Frappe Forum, https://discuss.frappe.io/t/erpnext-docker-discussion/10766
  39. A Deep Dive into Docker Compose - DEV Community, https://dev.to/alexmercedcoder/a-deep-dive-into-docker-compose-27h5
  40. Need Tutorial to Install ERPNext using docker - Frappe Forum, https://discuss.frappe.io/t/need-tutorial-to-install-erpnext-using-docker/119073
  41. ERPNext installation issues [docker] - Frappe Forum, https://discuss.frappe.io/t/erpnext-installation-issues-docker/133109
  42. docs/custom-apps.md · main · Christopher McKay / frappe_docker - GitLab, https://dev.egov.gy/christopher.mckay/frappe_docker/-/blob/main/docs/custom-apps.md
  43. frappe-docker for group development - Customize ERPNext, https://discuss.frappe.io/t/frappe-docker-for-group-development/140234
  44. How to frappe_docker install custom app? - Frappe Forum, https://discuss.frappe.io/t/how-to-frappe-docker-install-custom-app/111936
  45. bench backup - Documentation for Frappe Apps, https://docs.frappe.io/framework/user/en/bench/reference/backup
  46. Frappe Bench Backup and Restore Guide, https://discuss.frappe.io/t/frappe-bench-backup-and-restore-guide/148270
  47. Docker restore backup - Deployment - Frappe Forum, https://discuss.frappe.io/t/docker-restore-backup/85375
  48. LibreDocker / docker-erpnext - GitLab, https://gitlab.com/libredocker/docker-erpnext
  49. How do you back up your Docker Compose / container setup : r/selfhosted - Reddit, https://www.reddit.com/r/selfhosted/comments/1hwqfm9/how_do_you_back_up_your_docker_compose_container/
  50. Easy Automated Docker Volume Backups That Are Database Friendly, https://www.thepolyglotdeveloper.com/2025/05/easy-automated-docker-volume-backups-database-friendly/
  51. Restore ERPNext Docker Install from S3 Backup Tutorial - Technologie Hub Wien, https://technologiehub.at/project-posts/tutorial/restore-erpnext/
  52. Frappe ERPNext 14 — backup and restore | by Greg Hermo - Medium, https://greghermo.medium.com/frappe-erpnext-14-backup-and-restore-f67f6bfc52ab
  53. Docker erpnext update - Frappe Forum, https://discuss.frappe.io/t/docker-erpnext-update/116438
  54. How to Update Docker Single Compose Setup Including Custom Production Image?, https://discuss.frappe.io/t/how-to-update-docker-single-compose-setup-including-custom-production-image/115546
  55. ERPNext upgrade makes assets fail · Issue #1353 · frappe/frappe_docker - GitHub, https://github.com/frappe/frappe_docker/issues/1353
  56. The Ultimate Guide to Setting Up Traefik | by Sven van Ginkel - Medium, https://medium.com/@svenvanginkel/the-ultimate-guide-to-setting-up-traefik-650bd68ae633
  57. docs/single-server-example.md · main · Christopher McKay / frappe_docker - GitLab, https://dev.egov.gy/christopher.mckay/frappe_docker/-/blob/main/docs/single-server-example.md
  58. Docker : ErpNext can’t build due to some reason I can’t comprehend - NixOS Discourse, https://discourse.nixos.org/t/docker-erpnext-cant-build-due-to-some-reason-i-cant-comprehend/64238
  59. How to troubleshoot ‘Bind for 0.0.0.0:80 failed: port is already allocated’ error in Docker, https://labex.io/tutorials/docker-how-to-troubleshoot-bind-for-0-0-0-0-80-failed-port-is-already-allocated-error-in-docker-417726
  60. Failing to build ErpNext due to Port already allocated - Docker Community Forums, https://forums.docker.com/t/failing-to-build-erpnext-due-to-port-already-allocated/148140
  61. Failed: port is already allocated even with container down - Docker Community Forums, https://forums.docker.com/t/failed-port-is-already-allocated-even-with-container-down/148489
  62. 6 Ways To Fix 502 Bad Gateway In Nginx - RedSwitches, https://www.redswitches.com/blog/nginx-502-bad-gateway-error/
  63. nginx docker container: 502 bad gateway response - Stack Overflow, https://stackoverflow.com/questions/38346847/nginx-docker-container-502-bad-gateway-response
  64. ERPNext is not starting after a system restart - Frappe Forum, https://discuss.frappe.io/t/erpnext-is-not-starting-after-a-system-restart/119620
  65. Permission Denied on Docker Volumes - Reddit, https://www.reddit.com/r/docker/comments/18rc1eo/permission_denied_on_docker_volumes/
  66. ierturk/frappe - Docker Image, https://hub.docker.com/r/ierturk/frappe
  67. Issues Running Custom Image built with custom apps - ERPNext - Frappe Forum, https://discuss.frappe.io/t/issues-running-custom-image-built-with-custom-apps/118308
  68. Issues · frappe/frappe_docker - GitHub, https://github.com/frappe/frappe_docker/issues